package pl.poznan.put.cs.idss.viamdp;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;

import pl.poznan.put.cs.idss.viamdp.core.algorithm.IAlgorithm;
import pl.poznan.put.cs.idss.viamdp.core.algorithm.VIA;
import pl.poznan.put.cs.idss.viamdp.core.structures.AvailableField;
import pl.poznan.put.cs.idss.viamdp.core.structures.Map;
import pl.poznan.put.cs.idss.viamdp.ui.MainWindow;

/**
 * Główna klasa aplikacji. Wczytuje plik wejściowy (ścieżka jako pierwszy
 * argument) i decyduje w jakim trybie uruchomić aplikację (drugi argument
 * -console uruchamia w trybie tekstowym, w pozostałych przypadkach w trybie
 * graficznym).
 * 
 * @author Kornel Lewandowski
 * 
 */
public class App {

	/**
	 * Metoda początkowa. Inicjalizuje wymagane pola, rozpoczyna wczytywanie
	 * pliku. Po pomyślnym wczytaniu opisu świata przekazuje sterowanie do
	 * odpowiedniej metody.
	 * 
	 * @param args
	 */
	public static void main(String[] args) {

		System.out.println("Aplikacja wczytała się.");

		double correctMoveProbability = 0.0;
		double goBackMoveProbability = 0.0;
		double turnLeftMoveProbability = 0.0;
		double turnRightMoveProbability = 0.0;

		double terminalFieldValue = 0.0;
		double holeFieldValue = 0.0;
		double moveRewardValue = 0.0;

		double discountValue = 0.0;

		int sizeX = 0;
		int sizeY = 0;

		Map worldMap = new Map(10, 10);

		if (args.length > 0) {

			try {
				FileReader fr = new FileReader(args[0]);
				BufferedReader br = new BufferedReader(fr);
				String s;
				int i = 0;
				int mapLine = 0;
				while ((s = br.readLine()) != null) {
					String[] fields = s.split("\\s+");
					if (i == 0) {
						correctMoveProbability = new Double(fields[0]);
						goBackMoveProbability = new Double(fields[3]);
						turnLeftMoveProbability = new Double(fields[2]);
						turnRightMoveProbability = new Double(fields[2]);
					} else if (i == 1) {
						terminalFieldValue = new Double(fields[0]);
						holeFieldValue = new Double(fields[1]);
						moveRewardValue = new Double(fields[2]);
					} else if (i == 2) {
						discountValue = new Double(fields[0]);
					} else if (i == 3) {
						sizeX = new Integer(fields[0]);
						sizeY = new Integer(fields[1]);
						worldMap = new Map(sizeX, sizeY);
					} else {
						if (i == 4) {
							initMapValues(correctMoveProbability,
									goBackMoveProbability,
									turnLeftMoveProbability,
									turnRightMoveProbability,
									terminalFieldValue, holeFieldValue,
									moveRewardValue, discountValue, worldMap);
						}
						readMapLine(worldMap, s, mapLine);
						mapLine++;
					}
					i++;
				}
				fr.close();
				IAlgorithm algorithm = new VIA();
				algorithm.setMap(worldMap);
				if (args.length > 1 && args[1].equals("-console")) {
					runAlgorithmWithConsole(worldMap, algorithm);
				} else {
					runAlgorithmInWindow(worldMap, algorithm);
				}

			} catch (Exception e) {
				e.printStackTrace();
				System.err.println("Nie udało się otworzyć pliku");
			}

		} else {
			System.err.println("Brak pliku wejściowego!");
		}

	}

	/**
	 * Kontroluje przebieg algorytmu w oknie programu.
	 * 
	 * @param worldMap
	 * @param algorithm
	 */
	private static void runAlgorithmInWindow(Map worldMap, IAlgorithm algorithm) {
		new MainWindow(algorithm, worldMap);
	}

	/**
	 * Kontroluje przebieg algorytmu w konsoli.
	 * 
	 * @param worldMap
	 * @param algorithm
	 * @throws IOException
	 */
	private static void runAlgorithmWithConsole(Map worldMap,
			IAlgorithm algorithm) throws IOException {
		System.out.println();
		while (algorithm.checkIfEnd() == false) {

			BufferedReader bufferRead = new BufferedReader(
					new InputStreamReader(System.in));
			String command = bufferRead.readLine();

			if (command.equals("help") || command.equals("h")) {
				System.out.println("Wpisz 'step', aby wykonać krok algorytmu.");
				System.out
						.println("Wpisz 'quit', aby przerwać działanie z bieżącym wynikiem.");
			} else if (command.equals("step") || command.equals("s")) {
				algorithm.doStep();
				clearConsole();
				algorithm.getCurrentState().printMapAsText();
				System.out.println();
			} else if (command.equals("quit") || command.equals("q")) {
				System.out
						.println("Przerwano działanie algorytmu poleceniem 'quit'.");
				break;
			} else {
				System.err.println("Nie rozpoznane polecenie '" + command
						+ "'. Wpisz 'help', aby zobaczyć listę poleceń.");
			}

		}

		clearConsole();
		System.out.println("Koniec działania algorytmu. Ostateczny wynik:");
		System.out.println();
		worldMap.printMapAsText();
	}

	/**
	 * @param worldMap
	 * @param s
	 * @param mapLine
	 */
	private static void readMapLine(Map worldMap, String s, int mapLine) {
		if (s.length() == worldMap.getSizeY()) {
			for (int c = 0; c < s.length(); c++) {
				if (s.charAt(c) == '.') {

					double randomStartReward = AvailableField.LITTLE_VALUE;

					worldMap.setAvailableField(mapLine, c, randomStartReward);

				} else if (s.charAt(c) == 'W') {

					worldMap.setTerminalField(mapLine, c);

				} else if (s.charAt(c) == 'J') {

					worldMap.setHoleField(mapLine, c);

				} else {

					worldMap.setForbiddenField(mapLine, c);

				}
			}
		}
	}

	/**
	 * Ustawia odczytane parametry dotyczące swiata.
	 * 
	 * @param correctMoveProbability
	 * @param goBackMoveProbability
	 * @param turnLeftMoveProbability
	 * @param turnRightMoveProbability
	 * @param terminalFieldValue
	 * @param holeFieldValue
	 * @param moveRewardValue
	 * @param discountValue
	 * @param worldMap
	 */
	private static void initMapValues(double correctMoveProbability,
			double goBackMoveProbability, double turnLeftMoveProbability,
			double turnRightMoveProbability, double terminalFieldValue,
			double holeFieldValue, double moveRewardValue,
			double discountValue, Map worldMap) {
		worldMap.setCorrectMoveProbability(correctMoveProbability);
		worldMap.setGoBackMoveProbability(goBackMoveProbability);
		worldMap.setTurnLeftMoveProbability(turnLeftMoveProbability);
		worldMap.setTurnRightMoveProbability(turnRightMoveProbability);
		worldMap.setTerminalFieldValue(terminalFieldValue);
		worldMap.setHoleFieldValue(holeFieldValue);
		worldMap.setMoveReward(moveRewardValue);
		worldMap.setDiscountValue(discountValue);
	}

	/**
	 * Czyści konsole (dodaje 10 pustych linii sprawiając wrażenie czyszczenia
	 * okna).
	 */
	private static void clearConsole() {

		for (int i = 0; i < 10; i++) {
			System.out.println();
		}

	}

}
